package cn.jcby.app.lebu.service.impl;

import cn.jcby.app.config.AuthConfig;
import cn.jcby.app.config.QiNiuConfig;
import cn.jcby.app.excpt.enums.ExceptionMsgEnum;
import cn.jcby.app.lebu.controller.req.*;
import cn.jcby.app.lebu.controller.req.db.UserNearByDbReq;
import cn.jcby.app.lebu.controller.res.OtherUserInfoRes;
import cn.jcby.app.lebu.controller.res.UserLoginRes;
import cn.jcby.app.lebu.controller.res.UserNearByRes;
import cn.jcby.app.lebu.dao.UserDao;
import cn.jcby.app.lebu.dao.UserMotionPlanDao;
import cn.jcby.app.lebu.entity.UserEntity;
import cn.jcby.app.lebu.entity.UserInviteMotionRecordEntity;
import cn.jcby.app.lebu.entity.UserMotionPlanEntity;
import cn.jcby.app.lebu.service.*;
import cn.jcby.app.lebu.utils.*;
import cn.jcby.app.lebu.utils.sub.IdCardVerificationRequest;
import cn.jcby.app.util.ConstantKey;
import cn.jcby.app.util.JWTSUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.GeometryFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private UserTabService userTabService;
    @Autowired
    private JWTSUtils jwtUtils;
    @Autowired
    private QiNiuConfig qiNiuConfig;
    @Autowired
    private DictService dictService;
    @Autowired
    private UserMotionPlanDao userMotionPlanDao;
    @Autowired
    private UserInviteMotionRecordService userInviteMotionRecordService;
    @Autowired
    private DistributionTemplateService distributionTemplateService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private AuthConfig authConfig;


    @Value("${constants.captcha.length}")
    private Integer captchaLength;
    @Value("${constants.captcha.expire}")
    private Long captchaExpire;
    @Value("${spring.profiles.active:prod}")
    private String active;

    private static GoogleAuthenticatorConfig config = null ;

    @Override
    public PageUtils queryPage(BasicListReq params) {
        IPage<UserEntity> page = this.page(
                new Page<>(params.getPage(),params.getLimit()),
                Wrappers.<UserEntity>lambdaQuery()
        );

        return new PageUtils(page);
    }
    @Override
    public void add(UserEntity userReq){
        UserEntity user = new UserEntity() ;
        save(user) ;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBy(UpdateUserReq req){
        req.check();
        if(StringUtils.isNotBlank(req.getParentInviteCode())){
            Assert.isFalse(UserInfoUtil.getInviteCode().equals(req.getParentInviteCode()),"不能填写自己的邀请码");
        }
        //验证该验证码有效
        if(StringUtils.isNotBlank(req.getParentInviteCode())){
            UserEntity user = this.baseMapper.selectOne(Wrappers.<UserEntity>lambdaQuery().eq(UserEntity::getInviteCode, req.getParentInviteCode()));
            Assert.isTrue(user!= null, "邀请码不存在");
        }
        UserEntity user = new UserEntity() ;
        BeanUtils.copyProperties(req,user,"mobile","id","inviteCode") ;
        user.setId(UserInfoUtil.getUserId());
        updateById(user) ;
        //如果标签不为空，更新标签
        if (CollectionUtils.isNotEmpty(req.getTagList())){
            //清理原标签，添加新标签
            userTabService.updateTagesByUserId(user.getIdCard(),req.getTagList()) ;
        }
    }

    @Override
    public Result getVerifyCode(String mobile) {
        if (config == null){
            config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
                    .setTimeStepSizeInMillis(TimeUnit.SECONDS.toMillis(captchaExpire))
                    .setCodeDigits(captchaLength).build();
        }
        GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(config);
        Integer totpPassword = googleAuthenticator.getTotpPassword(mobile);
        
        //if ("prod".equals(active)){
            //TODO 调用第三方接口发送验证码
            {
                try {
                    smsService.sendSms(mobile,totpPassword.toString());
                    return Result.ok().put("data","发送成功") ;
                }catch (Exception e){
                    e.printStackTrace();
                    return Result.error(-1, e.getMessage()) ;
                }
            }
        //}
        //return Result.ok().put("data",totpPassword.toString()) ;
    }

    @Override
    public UserLoginRes login(LoginUserReq req, HttpServletResponse response) {
        UserLoginRes res = new UserLoginRes() ;
        if (config == null){
            config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
                   .setTimeStepSizeInMillis(captchaExpire)
                   .setCodeDigits(captchaLength).build();
        }
        GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(config);
        Integer totpPassword = googleAuthenticator.getTotpPassword(req.getMobile());
        Assert.isTrue(req.getCode().equalsIgnoreCase(totpPassword.toString()),"验证码错误或过期");

        UserEntity userEntity = userDao.selectOne(Wrappers.<UserEntity>lambdaQuery().eq(UserEntity::getMobile, req.getMobile()));
        //判断用户是否存在，存在就登录，不存在就注册后登录
        if (userEntity==null){
            userEntity = new UserEntity() ;
            userEntity.setMobile(req.getMobile());
            userEntity.setCreateTime(new Date());
            userEntity.setInviteCode(Constants.getInviteCode(req.getMobile()));
            userDao.insert(userEntity);
        }
        BeanUtils.copyProperties(userEntity,res);
        if (StringUtils.isNotBlank(userEntity.getFaceUrl())) {
            res.setFaceUrl(qiNiuConfig.getDomain() + "/" + userEntity.getFaceUrl());
        }
        //获取个人标签列表
        res.setTagList(userTabService.getTagListBy(userEntity.getId()));
        //添加运动频率字典
        if (StringUtils.isNotEmpty(res.getMotionFrequency())){
            res.setMotionFrequencyDesc(userTabService.getTagNameByCode(Constants.MOTION_FREQUENCY,res.getMotionFrequency()));
        }
        if (StringUtils.isNotEmpty(res.getConstellation())){
            res.setConstellationDesc(userTabService.getTagNameByCode(Constants.CONSTELLATION,res.getConstellation()));
        }
        if (StringUtils.isNotEmpty(res.getOccupation())){
            res.setOccupationDesc(userTabService.getTagNameByCode(Constants.OCCUPATION,res.getOccupation()));
        }
        String token = jwtUtils.generateJwtToken(userEntity.getId()+ ConstantKey.DELIMITER +userEntity.getMobile()+ConstantKey.DELIMITER +userEntity.getInviteCode());
        response.setHeader("Authorization", token);
        return res;
    }

    @Override
    public UserLoginRes infoById() {
        UserLoginRes res = new UserLoginRes() ;
        UserEntity userEntity = userDao.selectById(UserInfoUtil.getUserId());
        if (userEntity!=null){
            BeanUtils.copyProperties(userEntity,res);
            if (StringUtils.isNotBlank(userEntity.getFaceUrl())) {
                res.setFaceUrl(qiNiuConfig.getDomain() + "/" + userEntity.getFaceUrl());
            }
            //添加运动频率字典
            if (StringUtils.isNotEmpty(res.getMotionFrequency())){
                res.setMotionFrequencyDesc(userTabService.getTagNameByCode(Constants.MOTION_FREQUENCY,res.getMotionFrequency()));
            }
            if (StringUtils.isNotEmpty(res.getConstellation())){
                res.setConstellationDesc(userTabService.getTagNameByCode(Constants.CONSTELLATION,res.getConstellation()));
            }
            if (StringUtils.isNotEmpty(res.getOccupation())){
                res.setOccupationDesc(userTabService.getTagNameByCode(Constants.OCCUPATION,res.getOccupation()));
            }
            //获取个人标签列表
            res.setTagList(userTabService.getTagListBy(userEntity.getId()));
            if (userEntity.getTotalMileage()!=null){
                res.setTotalMileageStr(Constants.getMileageStr(userEntity.getTotalMileage()));
            }
        }
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result auth(AuthUserReq req) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        //TODO 实名认证，调用第三方接口
        IdCardVerificationRequest idCardVerificationRequest = new IdCardVerificationRequest();
        idCardVerificationRequest.setIdCard(req.getIdCard());
        idCardVerificationRequest.setName(req.getRealName());
        Result result = AuthRealNameUtils.auth(idCardVerificationRequest, authConfig);

        if (Integer.parseInt(result.get("code").toString())==0) {
            UserEntity userEntity = new UserEntity();
            userEntity.setRealName(req.getRealName());
            userEntity.setIdCard(req.getIdCard());
            userEntity.setAuthentication(1);
            userEntity.setId(UserInfoUtil.getUserId());;
            int count = userDao.updateById(userEntity);
            Assert.isFalse(count == 0, ExceptionMsgEnum.USER_AUTH_FAIL.getCode() + "");
        }
        return result;
    }

    @Override
    public OtherUserInfoRes otherInfoById(String userId) {
        OtherUserInfoRes res = new OtherUserInfoRes() ;
        //ta的基本信息
        UserEntity userEntity = userDao.selectById(userId);
        Assert.isFalse(userEntity == null,ExceptionMsgEnum.USER_NOT_EXIST.getCode()+"");
        BeanUtils.copyProperties(userEntity,res);
        //年龄计算
        if (userEntity.getBirthday()!=null){
            res.setAge(LocalDateUtils.getAge(userEntity.getBirthday().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()));
        }
        //星座、职业
        if (StringUtils.isNotEmpty(userEntity.getConstellation())){
            res.setConstellationDesc(userTabService.getTagNameByCode(Constants.CONSTELLATION,userEntity.getConstellation()));
        }
        if (StringUtils.isNotEmpty(userEntity.getOccupation())){
            res.setOccupationDesc(userTabService.getTagNameByCode(Constants.OCCUPATION,userEntity.getOccupation()));
        }
        //距离计算
        if (userEntity.getLatitude()!=null && userEntity.getLongitude()!=null){
            //判断当前登录人是否有上次登录记录
            UserEntity user = userDao.selectById(UserInfoUtil.getUserId());
            if (user.getLatitude()!=null && user.getLongitude()!=null){
                res.setDistance(Constants.getDistance(user.getLatitude(),user.getLongitude(),
                        userEntity.getLatitude(),userEntity.getLongitude()));
            }
        }
        //标签 TODO 一直报异常，没发现有问题，因为登录调用该方法正常，其他调用都会报错。wft
        res.setTagList(userTabService.getTagListBy(userEntity.getId()));
        //成绩
        List <UserMotionPlanEntity> personList = userMotionPlanDao.selectList(Wrappers.<UserMotionPlanEntity>lambdaQuery()
                .select(UserMotionPlanEntity::getParticipantUserId)
                .isNotNull(UserMotionPlanEntity::getParticipantUserId)
                .eq(UserMotionPlanEntity::getUserId, userId));
        res.setPersonCount(personList.size());
        //是否已被邀请
        UserInviteMotionRecordEntity one = userInviteMotionRecordService.getOne(new QueryWrapper<UserInviteMotionRecordEntity>().select("id")
                .eq("invited_user_id", userId)
                .eq("user_id", UserInfoUtil.getUserId()));
        res.setIsInvited(one!=null);
        return res ;
    }

    @Override
    public void updateLocation(UserLastPointReq req) {
        /*String format = String.format("POINT(%f %f)", req.getLongitude(), req.getLatitude());
        String s = GeoToolsUtils.WktToGeoJson(format);*/
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        UserEntity userEntity = new UserEntity() ;
        userEntity.setId(UserInfoUtil.getUserId());
        /*userEntity.setLatitude(req.getLatitude());
        userEntity.setLongitude(req.getLongitude());*/
        //userEntity.setLastPoint(geometryFactory.createPoint(new Coordinate(req.getLongitude(),req.getLatitude())).toText());
        //userEntity.setLastPoint(GeoToolsUtils.getPoint(req.getLongitude(),req.getLatitude()));
        userEntity.setLastPoint(req.getLastPoint());
        this.baseMapper.updateLastPoint(userEntity);
        //updateById(userEntity) ;
    }

    @Override
    public PageUtils nearBy(UserNearByReq req) {
        PageUtils pageUtils = new PageUtils(null,0,req.getPage(),req.getLimit());
        //有运动类型筛选，搜索所有有该类型计划的数据
        //查询当前人的最近一次上报坐标
        String pointStr = this.baseMapper.queryPointStr(UserInfoUtil.getUserId());
        if (StringUtils.isNotBlank(pointStr)) {
            UserNearByDbReq dbReq = new UserNearByDbReq();
            dbReq.setPointStr(pointStr);
            dbReq.setUserId(UserInfoUtil.getUserId());
            dbReq.setMotionType(req.getMotionType());
           //查询附近发布计划列表
            Integer count = this.baseMapper.queryNearByCount(dbReq);
            pageUtils = new PageUtils(null,count,req.getLimit(),req.getPage());
            if (count!=null && count > 0) {
                List<UserNearByRes> userNearByList = this.baseMapper.queryNearUserList(dbReq);
                if (CollectionUtils.isNotEmpty(userNearByList)){
                    //查询字典
                    userNearByList.stream().forEach(userNearByRes -> {
                        //职业
                        if (StringUtils.isNotBlank(userNearByRes.getOccupation())) {
                            userNearByRes.setOccupationDesc(dictService.getTagNameByCode(Constants.OCCUPATION, userNearByRes.getOccupation()));
                        }
                        //星座
                        if (StringUtils.isNotBlank(userNearByRes.getConstellation())) {
                            userNearByRes.setConstellationDesc(dictService.getTagNameByCode(Constants.CONSTELLATION, userNearByRes.getConstellation()));
                        }
                        userNearByRes.setTotalMileageDesc(Constants.getMileageStr(userNearByRes.getTotalMileage()));
                        if (StringUtils.isNotBlank(userNearByRes.getDistance())){
                            userNearByRes.setDistance(Constants.getMileageStrEn(BigDecimal.valueOf(Double.valueOf(userNearByRes.getDistance()))));
                        }
                        userNearByRes.setHasPlan(false);
                        if (userNearByRes.getPlanCount()!=null && userNearByRes.getPlanCount()>0){
                            userNearByRes.setHasPlan(true);
                        }
                    });
                }
                pageUtils.setList(userNearByList);
            }
        }
        return pageUtils ;
    }

    @Override
    public void bindOpenId(String openId) {

        this.baseMapper.bindOpenId(UserInfoUtil.getUserId(),openId) ;
    }


    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
                    .setTimeStepSizeInMillis(2000L)
                    .setCodeDigits(8).build();
            GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator(config);
            Integer totpPassword = googleAuthenticator.getTotpPassword("123");
            System.out.println(totpPassword);
            Thread.sleep(1000);
        }

    }
}